package awk.thread.future;

import java.util.concurrent.*;

public class CompletableFutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.submit(() -> {
            System.out.println("executorService 是否为守护线程 :" + Thread.currentThread().isDaemon());
            return null;
        });
        //supplyAsync(Supplier<U> supplier) 接受一个lambda函数，还可以再接受一个执行器,如果不
        //传递执行器，则使用默认的执行器：Executor asyncPool = useCommonPool ? ForkJoinPool.commonPool() : new ThreadPerTaskExecutor()
        //supplyAsync 即接收 supply类型的lambda函数，supply是有返回值的，
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("this is lambda supplyAsync");
            System.out.println("supplyAsync 是否为守护线程 " + Thread.currentThread().isDaemon());
            try {
                TimeUnit.SECONDS.sleep(5);
                System.out.println("this lambda is executed by forkJoinPool");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "result1";
        });
        //runAsync 即接受一个 run 类型的lambda函数，run是void返回值的
        CompletableFuture<Void> completableFuture2 = CompletableFuture.runAsync(() -> {
            System.out.println("this is lambda runAsync");
            System.out.println("runAsync 使用 executorService 时是否为守护线程 : " + Thread.currentThread().isDaemon());
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        //不用get方法阻塞主线程会导致任务执行中断(具体表现就是 TimeUnit.SECONDS.sleep(2) 不会执行)
        //原因是所有的非守护线程都执行完毕，则主线程退出
        //如果completableFuture.get()获取返回值，则因为get方法是阻塞的，所以非守护线程在获取get时不退出，主线程也就不会退出
        //System.out.println(completableFuture1.get());
        //System.out.println(completableFuture2.get());


        //这里 completableFuture3 也执行耗时任务，但是不使用get()方法获取结果，但是程序还是会正确执行(TimeUnit.SECONDS.sleep(2) 会执行)
        //因为手动指定了线程池executorService（如果不指定则会使用默认的 ForkJoinPool 线程池）
        //而手动指定的线程池中的线程属于非守护线程，非守护线程不退出，主线程就不会退出，
        // 然后completableFuture1 和 completableFuture2 在主线程不退出时就会继续往下执行
        CompletableFuture<String> completableFuture3 = CompletableFuture.supplyAsync(() -> {
            System.out.println("this is lambda runAsync");
            System.out.println("runAsync 使用 executorService 时是否为守护线程 : " + Thread.currentThread().isDaemon());
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println("this lambda is executed by executorService");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "hello";
        },executorService);

        //anyOf是当入参的completableFuture组中有一个任务执行完毕就返回。返回结果是第一个完成的任务的结果。
        CompletableFuture.anyOf(completableFuture1,completableFuture2,completableFuture3);
        //allOf就是所有任务都完成时返回。但是是个Void的返回值。
        CompletableFuture.allOf(completableFuture1,completableFuture2,completableFuture3);

        //执行 whenComplete 方法的线程是哪个？
        //如果触碰到whenComplete方法时completableFuture3已经执行完毕，则执行的线程就会释放，此时whenComplete会由主线程执行
        //如果触碰到whenComplete方法时completableFuture3还没有执行完毕，则因为该线程还存活，此时whenComplete会和completableFuture3公用同一个线程
        completableFuture3.whenComplete((s,e)->{
            try {
                //此处线程休息2S,下面的task1 会继续执行，则说明task1是主线程执行的，
                //而whenComplete是有completableFuture3的线程执行的
                Thread.sleep(2);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            //(s,e) s代表任务正常执行时的结果，e代表任务执行异常时的异常
            if (s != null){
                System.out.println("正确完成输出结果 " + s);
            }
            if (e != null){
                System.out.println("异常输出结果 " + s);
            }
        });

        System.out.println("task1");


        //completableFuture3.then()
        //线程池必须手动调用shutdown(),否则线程池会一直维护线程池的initNum个线程，这些线程都是非守护线程，
        //非守护线程一直存在，则主线程不会退出
        executorService.shutdown();
    }
}
